package com.yeskery.nut.application.jetty;

import com.yeskery.nut.application.NutHttpServlet;
import com.yeskery.nut.application.NutServer;
import com.yeskery.nut.application.NutServerConfigure;
import com.yeskery.nut.core.NutException;
import com.yeskery.nut.util.ClassUtils;
import com.yeskery.nut.websocket.WebSocketConfiguration;
import com.yeskery.nut.websocket.WebSocketServer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.logging.Logger;

/**
 * 基于Jetty的HttpServer实现HTTP服务
 * @author sprout
 * @version 1.0
 * 2022-08-05 01:11
 */
public class JettyNutServer implements NutServer, WebSocketServer {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(JettyNutServer.class.getName());

    /** WebSocket依赖检查class名称 */
    private static final String[] WS_DEPEND_CHECK_CLASS_NAMES = {"org.eclipse.jetty.websocket.api.Session",
            "org.eclipse.jetty.websocket.jsr356.server.ServerContainer"};

    /** cg-lib依赖检查class名称 */
    private static final String CG_LIB_DEPEND_CHECK_CLASS_NAME = "net.sf.cglib.proxy.Enhancer";

    /** JettyServer对象 */
    private Server server;

    /** Jetty 连接器 */
    private ServerConnector connector;

    /** JettyServer线程池对象 */
    private QueuedThreadPool threadPool;

    /** WebSocket配置对象集合 */
    private Collection<WebSocketConfiguration> webSocketConfigurations;

    /** 是否安全方式启动 */
    protected boolean secure = false;

    @Override
    public void startServer(NutServerConfigure nutServerConfigure) throws Exception {
        JettyWebSocketLoader jettyWebSocketLoader = new JettyWebSocketLoaderFactory(
                nutServerConfigure.getNutApplication().getApplicationContext()).buildJettyWebSocketLoader();

        threadPool = new QueuedThreadPool();
        threadPool.setName(getThreadPoolName());

        server = new Server(threadPool);
        connector = buildServerConnector(server);
        connector.setPort(nutServerConfigure.getPort());
        server.addConnector(connector);

        ServletHolder servletHolder = new ServletHolder(new NutHttpServlet(nutServerConfigure.getNutApplication(),
                nutServerConfigure.getServerContext(), nutServerConfigure.getDispatcher(),
                nutServerConfigure.getSessionManager(), nutServerConfigure.getServerRequestConfiguration(), this));
        ServletContextHandler context = new ServletContextHandler();
        context.setAttribute("org.eclipse.jetty.websocket.jsr356", Boolean.TRUE);
        context.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize", -1);
        context.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys", -1);
        context.setMaxFormContentSize(-1);
        context.setMaxFormKeys(-1);

        context.setContextPath( "/" );
        context.addServlet(servletHolder, "/*");

        server.setHandler(context);
        if (enable()) {
            jettyWebSocketLoader.registerEndpoints(context, webSocketConfigurations);
        }

        logger.info(getServerStartedTip(nutServerConfigure.getPort(), "Jetty", secure));
        server.start();

        server.join();
    }

    @Override
    public void checkDependExist() throws Exception {
        if (Arrays.stream(WS_DEPEND_CHECK_CLASS_NAMES).anyMatch(s -> !ClassUtils.isExistTargetClass(s))) {
            throw new NutException("Jetty Server Used WebSocket, Such As @WebSocket Annotation, But Not Found " +
                    "Jetty WebSocket Library, Check Has Been Import websocket-server Or javax-websocket-server-impl?");
        }
        if (!ClassUtils.isExistTargetClass(CG_LIB_DEPEND_CHECK_CLASS_NAME)) {
            throw new NutException("Jetty Server Used WebSocket, Such As @WebSocket Annotation, But Not Found cg-lib " +
                    "Library, Check Has Been Import cglib.cglib-nodep?");
        }
    }

    @Override
    public void registerEndpoints(Collection<WebSocketConfiguration> webSocketConfigurations) {
        this.webSocketConfigurations = webSocketConfigurations;
    }

    @Override
    public boolean enable() {
        return webSocketConfigurations != null && !webSocketConfigurations.isEmpty();
    }

    @Override
    public void close() throws IOException {
        try {
            if (server != null) {
                server.stop();
            }
            if (connector != null) {
                connector.stop();
            }
            if (threadPool != null) {
                threadPool.stop();
            }
        } catch (Exception e) {
            throw new IOException("Jetty Server Close Failure.", e);
        }
    }

    /**
     * 构建Jetty 连接器
     * @param server JettyServer对象
     * @return etty 连接器
     * @throws Exception 构建异常
     */
    protected ServerConnector buildServerConnector(Server server) throws Exception {
        return new ServerConnector(server);
    }

    /**
     * 获取JettyServer线程池名称
     * @return JettyServer线程池名称
     */
    protected String getThreadPoolName() {
        return "JettyServer";
    }
}
